#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<cre2.h>

// 排序比较函数
int cmp(const void *p, const void *q);
// 将牌中的字母变为大写
void upper_hand(char *in_hand);
// 将牌分为用#分隔的等级和花色两部分，并对两部分分别排序
void sort_hand(char *in_hand);
// 牌型判断
int classify_poker(const char *in_hand);

// 测试/驱动
int main()
{
    int i, ret;
    char in_hand[][12] = {"2s5s4s3s6s", "8cas7cad3h", "6s2d9c4hts",
                          "asqsjsksts", "asqcjdkhts", "1s5sjsksts"
                         };
    char *poker_type[] = {"Straight flush", "Four of a kind", "Full house",
                          "Flush", "Straight", "Three of a kind",
                          "Two pair", "One pair", "High card"};

    for(i = 0; i < sizeof(in_hand) / sizeof(in_hand[0]); i++)
    {
        upper_hand(in_hand[i]);
        sort_hand(in_hand[i]);
        printf("%s: ", in_hand[i]);
        ret = classify_poker(in_hand[i]);
        puts(poker_type[ret]);
    }

    return 0;
}

// 牌型判断
// 要求等级与花色用#分隔且要求等级与花色分别进行了排序
int classify_poker(const char *in_hand)
{
    // 正则表达式数组
    // 由于CRE2正则引擎不支持类似\1的反向引用
    // 因此需要直接写出正则表达式
    const char *pat[] = {
      "(2345A|23456|34567|45678|56789|6789T|"
      "789JT|89JQT|9JKQT|AJKQT)"
      "#(S{5}|H{5}|D{5}|C{5})", // #0 同花顺
      "(2{4}|3{4}|4{4}|5{4}|6{4}|7{4}|8{4}|"
      "9{4}|T{4}|J{4}|Q{4}|K{4}|A{4}).*#.*", // #1 四张
      "((2{3}|3{3}|4{3}|5{3}|6{3}|7{3}|8{3}|" // 葫芦
      "9{3}|T{3}|J{3}|Q{3}|K{3}|A{3})"
      "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
      "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}))|"      // 三张加一对
      "((2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
      "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2})"
      "(2{3}|3{3}|4{3}|5{3}|6{3}|7{3}|8{3}|"
      "9{3}|T{3}|J{3}|Q{3}|K{3}|A{3})).*#.*", // 一对加三张
      ".*#(S{5}|H{5}|D{5}|C{5})", // #3 同花
      "(2345A|23456|34567|45678|56789|6789T|"
      "789JT|89JQT|9JKQT|AJKQT)#.*", // #4 顺子
      "(2{3}|3{3}|4{3}|5{3}|6{3}|7{3}|8{3}|"
      "9{3}|T{3}|J{3}|Q{3}|K{3}|A{3}).*#.*", // #5 三张
      "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
      "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}).*"
      "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
      "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}).*#.*",  // #6 两对
      "(2{2}|3{2}|4{2}|5{2}|6{2}|7{2}|8{2}|"
      "9{2}|T{2}|J{2}|Q{2}|K{2}|A{2}).*#.*"  // #7 一对
    };

    // 正则对象指针
    cre2_regexp_t *rex;
    // 选项对象指针
    cre2_options_t *opt;
    int i, c;
    size_t in_len = strlen(in_hand);

    for(i = 0; i < 8; i++)
    {
        // 创建选项对象
        opt = cre2_opt_new();

        // 编译正则表达式，构建正则对象
        rex = cre2_new(pat[i], strlen(pat[i]), opt);
        if(cre2_error_code(rex))
        {
            exit(EXIT_FAILURE);
        }

        // 执行匹配
        c = cre2_match(rex, in_hand, in_len, 0, in_len,
                       CRE2_UNANCHORED, NULL, 0);

        // 释放空间
        cre2_delete(rex);
        cre2_opt_delete(opt);

        if(c == 1)
        {
            // 匹配成功，返回类型码
            return i;
        }
    }

    // 未匹配成功，返回其它类型码
    return i;
}

// 将牌用#分为等级和花色两部分，并对两部分进行排序
void sort_hand(char *s)
{
    char face[6] = {0};
    char suit[6] = {0};
    int i = 0;
    char *p = s;

    while(*p)
    {
        if(i % 2 == 0)
        {
            face[i / 2] = *p;
        }
        else
        {
            suit[i / 2] = *p;
        }
        i++;
        p++;
    }

    qsort(face, strlen(face), 1, cmp);
    qsort(suit, strlen(suit), 1, cmp);
    strcpy(s, face);
    strcat(s, "#");
    strcat(s, suit);
}

// 将字符串中的小写字母变换为大写，其它字符不变
void upper_hand(char *s)
{
    while(*s)
    {
        if(*s >= 'a' && *s <= 'z')
        {
            *s ^= 0x20; // 转换为大写
        }
        s++;
    }
}

// 排序比较函数
int cmp(const void *p, const void *q)
{
    char ch_p = *((char*)p);
    char ch_q = *((char*)q);

    return (ch_p > ch_q) - (ch_p < ch_q);
}
